package io.terminus.furniture.requisition.func.purapply.assemble;

import com.google.common.collect.Lists;
import io.terminus.furniture.requisition.dict.*;
import io.terminus.furniture.requisition.ext.purapply.ConvertSourceLineToPreProductPlanExt;
import io.terminus.furniture.requisition.model.base.SupplierBO;
import io.terminus.furniture.requisition.model.order.*;
import io.terminus.furniture.requisition.tmodel.ProductivityChangeTO;
import io.terminus.furniture.requisition.tmodel.PurchaseApplyOrderSavedTO;
import io.terminus.furniture.requisition.utils.DateUtils;
import io.terminus.trantor.module.base.model.User;
import io.terminus.trantorframework.api.annotation.FunctionImpl;
import io.terminus.trantorframework.api.type.Currency;
import io.terminus.trantorframework.sdk.sql.DS;
import io.terminus.trantorframework.sdk.util.ModelUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 居士浩(居士)
 */
@Slf4j
@FunctionImpl
@RequiredArgsConstructor
public class AssembleSaveTOForPurchaseSourceResultLineEditFuncImpl implements AssembleSaveTOForPurchaseSourceResultLineEditFunc {
    private final ConvertSourceLineToPreProductPlanExt convertSourceLineToPreProductPlanExt;

    @Override
    public PurchaseApplyOrderSavedTO execute(PurchaseSourcingResultLineBO sourcingResultLine) {
        PurchaseApplyOrderSavedTO savedTO = new PurchaseApplyOrderSavedTO();
        if (!sourcingResultLine.isModifyWhenEdit()) {
            return savedTO;
        }
        // 待更新的寻源明细行; 注意: 除了合并寻源结果行的场景，sourceLine都是更新当前行（合并场景更新目标行，删除当前行）
        // 以编辑提交的参数完全更新
        PurchaseSourcingResultLineBO updateSourceLine = initUpdateSourceLine(sourcingResultLine);
        if (sourcingResultLine.getSupplierModifyYn() || sourcingResultLine.getSourceQtyModifyYn()) {
            // 供应商或者寻源数量有变更时，需要更新采购申请单行的实际申请数
            assembleForUpdateApplyLine(sourcingResultLine, savedTO);
        }
        // 供应商有变更
        if (sourcingResultLine.getSupplierModifyYn()) {
            // 供应商有变更时的保存参数构建
            assembleForSupplierModify(sourcingResultLine, updateSourceLine, savedTO);
        }
        // 如果寻源数量有变更
        if (sourcingResultLine.getSourceQtyModifyYn()) {
            // 如果是编辑成功结果行 && 供应商没有发生变化，需要更新当前供应商行的采购价
            if (isEditSuccessSourceLine(sourcingResultLine) && !sourcingResultLine.getSupplierModifyYn()) {
                assembleForUpdateCurrentSourceResult(sourcingResultLine, savedTO);
            }
        }
        // 如果是寻源失败行的更新
        if (!isEditSuccessSourceLine(sourcingResultLine)) {
            // 清空失败备注
            updateSourceLine.setRemark(StringUtils.EMPTY);
            // 状态更新为已寻源
            updateSourceLine.setStatus(PurchaseSourcingResultLineStatusDict.SOURCED);
        }
        //--------------------更新的寻源结果明细
        savedTO.setUpdateSourceResultLineList(Collections.singletonList(updateSourceLine));
        // 寻源失败行不存在预排生产计划
        if (isEditSuccessSourceLine(sourcingResultLine)) {
            // 将之前的预排生产计划全部删除
            List<PrepareProductPlanOrderBO> deletePrePlanList = DS.findAll(PrepareProductPlanOrderBO.class,
                    "id", "sourcingResultLine = ?", sourcingResultLine.getId());
            //--------------------删除的预排生产计划
            savedTO.getDeletePreProductPlanOrderList().addAll(deletePrePlanList);
        }
        // 寻源明细行需要重新计算预排生产计划
        List<PrepareProductPlanOrderBO> createPrePlanList = convertSourceLineToPreProductPlanExt.execute(sourcingResultLine.getApplyOrder(), Collections.singletonList(updateSourceLine));
        //--------------------创建的预排生产计划
        savedTO.setCreatePreProductPlanOrderList(createPrePlanList);
        // 组装产能相关参数，释放、占用...
        assembleForEditProductivity(sourcingResultLine, savedTO);
        return savedTO;
    }

    /**
     * 1. 寻源失败明细编辑，占用选择的供应商
     * 2. 寻源成功明细编辑，释放原占用记录，以新的请求占用
     *
     * @param sourcingResultLine
     * @param savedTO
     */
    private void assembleForEditProductivity(PurchaseSourcingResultLineBO sourcingResultLine,
                                             PurchaseApplyOrderSavedTO savedTO) {
        List<ProductivityChangeTO> occupyList = Lists.newArrayList();
        List<ProductivityChangeTO> cancelList = Lists.newArrayList();
        // 关联的采购申请单
        PurchaseApplyOrderBO relateApplyOrder = DS.findById(PurchaseApplyOrderBO.class,
                sourcingResultLine.getPreviousSourceResultLine().getApplyOrder().getId());
        if (isEditSuccessSourceLine(sourcingResultLine)) {
            // 如果编辑寻源成功明细，需要释放已占用产能
            ProductivityChangeTO cancelTO = new ProductivityChangeTO();
            cancelTO.setSourceOrderId(relateApplyOrder.getId());
            cancelTO.setSourceSubOrderId(sourcingResultLine.getId());
            cancelTO.setSourceEventTypeDict(ProductivitySourceEventTypeDict.CANCEL);
            cancelTO.setSourceOrderType(ProductivitySourceOrderTypeDict.PURCHASE_APPLY);
            cancelList.add(cancelTO);
        }
        // 占用产能
        ProductivityChangeTO occupyTO = new ProductivityChangeTO();
        occupyTO.setProductivityDate(sourcingResultLine.getDemandDate());
        occupyTO.setSupplier(sourcingResultLine.getSupplier());
        occupyTO.setProductivityModeDict(ProductivityChangeModeDict.LOOSE);
        occupyTO.setRequestQuota(sourcingResultLine.getPurchasePrc().getValue().multiply(sourcingResultLine.getSourceQty()));
        occupyTO.setSourceEventTypeDict(ProductivitySourceEventTypeDict.OCCUPY);
        occupyTO.setSourceOrderType(ProductivitySourceOrderTypeDict.PURCHASE_APPLY);
        occupyTO.setSourceOrderId(relateApplyOrder.getId());
        occupyTO.setSourceOrderCode(relateApplyOrder.getApplyOrderCode());
        occupyTO.setSourceSubOrderId(sourcingResultLine.getId());
        occupyList.add(occupyTO);

        //--------------------待释放和占用的产能
        savedTO.setCancelOfEditProductivityList(cancelList);
        savedTO.setOccupyOfEditProductivityList(occupyList);
    }

    /**
     * 编辑当前供应商行明细时，如果涉及数量更新，需要更新供应商行上的采购价
     *
     * @param sourcingResultLine
     * @param savedTO
     */
    private void assembleForUpdateCurrentSourceResult(PurchaseSourcingResultLineBO sourcingResultLine,
                                                      PurchaseApplyOrderSavedTO savedTO) {
        PurchaseSourcingResultLineBO previousLine = sourcingResultLine.getPreviousSourceResultLine();
        PurchaseSourcingResultBO updateSourceResult = new PurchaseSourcingResultBO();
        updateSourceResult.setId(sourcingResultLine.getSourcingResult().getId());
        // 原采购总价
        Currency previousAmt = previousLine.getPurchasePrc().multiply(previousLine.getSourceQty());
        // 当前采购总价
        Currency currentAmt = sourcingResultLine.getPurchasePrc().multiply(sourcingResultLine.getSourceQty());
        // 采购总价变化量
        Currency deltaAmt = currentAmt.subtract(previousAmt);
        updateSourceResult.setPurchaseAmt(sourcingResultLine.getSourcingResult().getPurchaseAmt().add(deltaAmt));
        //--------------------待更新的寻源供应商行
        savedTO.getUpdateSourceResultList().add(updateSourceResult);
    }

    /**
     * 构建待更新的采购明细
     *
     * @param sourcingResultLine
     * @param savedTO
     */
    private void assembleForUpdateApplyLine(PurchaseSourcingResultLineBO sourcingResultLine,
                                            PurchaseApplyOrderSavedTO savedTO) {
        PurchaseSourcingResultLineBO previousSourceLine = sourcingResultLine.getPreviousSourceResultLine();
        // DB中的采购申请单数据
        PurchaseApplyOrderLineBO applyOrderLineDB = DS.findById(PurchaseApplyOrderLineBO.class, previousSourceLine.getApplyOrderLine().getId());
        // 关联的采购申请单行中的实际申请数Map
        Map<String, BigDecimal> actualApplyQtyMap = applyOrderLineDB.getActualApplyQtyMap();
        String targetSupplierKey = sourcingResultLine.getSupplier().getId() + "-" + sourcingResultLine.getSupplier().getSupplierName();
        // 判断是否是寻源成功行的编辑
        if (isEditSuccessSourceLine(sourcingResultLine)) {
            actualApplyQtyMap.keySet().removeIf(supplierStr -> {
                String supplierId = supplierStr.split("-")[0];
                // 不管有没有更新，先将原供应商ID的数据清除
                return supplierId.equals(previousSourceLine.getSupplier().getId().toString());
            });
            actualApplyQtyMap.put(targetSupplierKey, sourcingResultLine.getSourceQty());
        } else {
            // 如果是失败行编辑，直接增加一部分数量
            actualApplyQtyMap.put(targetSupplierKey, actualApplyQtyMap.getOrDefault(targetSupplierKey, BigDecimal.ZERO).add(sourcingResultLine.getSourceQty()));
        }
        // 总实际申请数
        BigDecimal sumActualApplyQty = actualApplyQtyMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        PurchaseApplyOrderLineBO updatePurchaseApplyLine = new PurchaseApplyOrderLineBO();
        updatePurchaseApplyLine.setId(applyOrderLineDB.getId());
        updatePurchaseApplyLine.setActualApplyQty(sumActualApplyQty);
        updatePurchaseApplyLine.setActualApplyQtyMap(actualApplyQtyMap);
        //--------------------更新的采购申请单行
        savedTO.setUpdatePurchaseApplyOrderLineList(Collections.singletonList(updatePurchaseApplyLine));
    }

    /**
     * 是否是寻源成功行编辑
     *
     * @param sourceResultLine
     * @return
     */
    private boolean isEditSuccessSourceLine(PurchaseSourcingResultLineBO sourceResultLine) {
        return PurchaseSourcingResultLineStatusDict.SOURCED.equals(sourceResultLine.getStatus());
    }

    /**
     * 供应商变更时的参数处理
     *
     * @param sourcingResultLine
     * @param updateSourceLine
     * @param savedTO
     */
    private void assembleForSupplierModify(PurchaseSourcingResultLineBO sourcingResultLine,
                                           PurchaseSourcingResultLineBO updateSourceLine,
                                           PurchaseApplyOrderSavedTO savedTO) {
        // 判断是否存在可以合并的供应商行
        PurchaseSourcingResultBO targetSourceResultBO = DS.findOne(PurchaseSourcingResultBO.class,
                // 排除作废状态
                "*,sourcingResultLines.*", "`applyOrder` = ? AND `supplier` = ? AND `status` != ?",
                sourcingResultLine.getApplyOrder().getId(), sourcingResultLine.getSupplier().getId(), PurchaseSourcingResultStatusDict.INVALID);
        if (targetSourceResultBO == null) {
            // 新增寻源供应商行
            PurchaseSourcingResultBO createSourceResultBO = new PurchaseSourcingResultBO();
            createSourceResultBO.setId(DS.nextId(PurchaseSourcingResultBO.class));
            createSourceResultBO.setApplyOrder(sourcingResultLine.getApplyOrder());
            createSourceResultBO.setStatus(PurchaseSourcingResultStatusDict.CREATED);
            createSourceResultBO.setPurchaseAmt(sourcingResultLine.getPurchasePrc().multiply(sourcingResultLine.getSourceQty()));
            createSourceResultBO.setSupplier(sourcingResultLine.getSupplier());
            // 跟单员；寻源失败行没有关联供应商行，需要实时查询
            User merchandiser = sourcingResultLine.getSourcingResult() == null ?
                    DS.findById(SupplierBO.class, sourcingResultLine.getSupplier().getId()).getMerchandiser() :
                    sourcingResultLine.getSourcingResult().getMerchandiser();
            createSourceResultBO.setMerchandiser(merchandiser);
            // 关联到新的供应商行
            updateSourceLine.setSourcingResult(createSourceResultBO);
            //--------------------待创建的寻源供应商行
            savedTO.setCreateSourceResultList(Collections.singletonList(createSourceResultBO));
        } else {
            // 更新寻源供应商行
            PurchaseSourcingResultBO updateSourceResultBO = new PurchaseSourcingResultBO();
            updateSourceResultBO.setId(targetSourceResultBO.getId());
            Currency purchaseAmt = targetSourceResultBO.getPurchaseAmt()
                    .add(sourcingResultLine.getPurchasePrc().multiply(sourcingResultLine.getSourceQty()));
            updateSourceResultBO.setPurchaseAmt(purchaseAmt);
            //--------------------待更新的寻源供应商行
            savedTO.setUpdateSourceResultList(Collections.singletonList(updateSourceResultBO));
            Map<Long, PurchaseSourcingResultLineBO> targetSourceLineMap = targetSourceResultBO.getSourcingResultLines().stream()
                    // 排除作废状态
                    .filter(e -> !e.getStatus().equals(PurchaseSourcingResultLineStatusDict.INVALID))
                    .collect(Collectors.toMap(e -> e.getParcel().getId(), Function.identity()));
            if (targetSourceLineMap.containsKey(sourcingResultLine.getParcel().getId())) {
                // 目标行中有相同包件，则进行数量累加
                PurchaseSourcingResultLineBO targetSourceLine = targetSourceLineMap.get(sourcingResultLine.getParcel().getId());
                updateSourceLine.setId(targetSourceLine.getId());
                updateSourceLine.setSourceQty(targetSourceLine.getSourceQty().add(sourcingResultLine.getSourceQty()));
                // 需求时间取小
                updateSourceLine.setDemandDate(DateUtils.min(sourcingResultLine.getDemandDate(), targetSourceLine.getDemandDate()));
                // 合并到目前line中，原line删除掉
                PurchaseSourcingResultLineBO deleteSourceLine = new PurchaseSourcingResultLineBO();
                deleteSourceLine.setId(sourcingResultLine.getId());
                //--------------------删除的寻源明细行
                savedTO.setDeleteSourceResultLineList(Collections.singletonList(deleteSourceLine));
                // 删除目标line所关联的预排生产计划，后面重新生成
                List<PrepareProductPlanOrderBO> deletePrePlanList = DS.findAll(PrepareProductPlanOrderBO.class,
                        "id", "sourcingResultLine = ?", targetSourceLine.getId());
                //--------------------删除的预排生产计划
                savedTO.getDeletePreProductPlanOrderList().addAll(deletePrePlanList);
            } else {
                // 没有可合并的明细行，关联到新的供应商行
                updateSourceLine.setSourcingResult(targetSourceResultBO);
            }
        }

        // 判断是否是寻源成功行的编辑
        if (isEditSuccessSourceLine(sourcingResultLine)) {
            // 如果供应商行只有当前一条明细，要将供应商行删掉
            List<PurchaseSourcingResultLineBO> previousSourceLines = DS.findAll(PurchaseSourcingResultLineBO.class,
                    "id", "`sourcingResult` = ?",
                    sourcingResultLine.getSourcingResult().getId());
            if (previousSourceLines.size() <= 1) {
                // 待删除的寻源供应商行
                PurchaseSourcingResultBO deleteSourceResultBO = new PurchaseSourcingResultBO();
                deleteSourceResultBO.setId(sourcingResultLine.getSourcingResult().getId());
                //--------------------待删除的寻源供应商行
                savedTO.setDeleteSourceResultList(Collections.singletonList(deleteSourceResultBO));
            }
        }

    }

    /**
     * 初始化编辑的明细行
     *
     * @param editSourceLine
     * @return
     */
    private PurchaseSourcingResultLineBO initUpdateSourceLine(PurchaseSourcingResultLineBO editSourceLine) {
        PurchaseSourcingResultLineBO updateSourceLine = ModelUtils.convert(editSourceLine, PurchaseSourcingResultLineBO.class);

        return updateSourceLine;
    }
}
